package com.derbysoft.lowcostadapter.dswitch2.service.impl;

import com.derbysoft.lowcostadapter.commons.model.Hotel;
import com.derbysoft.lowcostadapter.commons.model.RoomStay;
import com.derbysoft.lowcostadapter.commons.redis.service.RoomStayCacheService;
import com.derbysoft.lowcostadapter.commons.redis.utils.KeyValueHelper;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.dswitch2.service.RoomStayService;
import com.derbysoft.lowcostadapter.schedule.parse.ParseRoomStayService;
import com.derbysoft.remote.dswitch.core.GenericRQ;
import com.derbysoft.remote.dswitch.core.GenericRS;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service("roomStayService")
public class RoomStayServiceImpl implements RoomStayService<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> {
    private static final int DELAY_ONE_DAY = -24;
    @Autowired
    private RoomStayCacheService roomStayCacheService;

    @Autowired
    private ParseRoomStayService parseRoomStayService;

    private static Logger logger = LoggerFactory.getLogger(RoomStayServiceImpl.class);

    @Override
    public GenericRS<HotelAvailRS> filter(GenericRQ<HotelAvailRQ> rq, GenericRS<HotelAvailRS> rs, Hotel hotel) {
        if (parseRoomStayService.isParsing()) {
            logger.info("parseRoomStayService is running ...");
            return null;
        }

        HotelAvailCriteriaDTO availCriteria = rq.getPayload().getAvailCriteria();
        StayDateRangeDTO stayDateRangeDTO = availCriteria.getStayDateRange();
        List<AvailRoomStayDTO> availRoomStayDTOs = new ArrayList<AvailRoomStayDTO>();

        int los = stayDateRangeDTO.lengthOfStay();

        for (AvailRoomStayDTO availRoomStayDTO : rs.getPayload().getHotelAvailRoomStay().getRoomStays()) {
            String roomTypeId = availRoomStayDTO.getRoomType().getCode();
            if (!roomStayRestricted(stayDateRangeDTO, hotel.getPropertyID(), roomTypeId, los)) {
                availRoomStayDTOs.add(availRoomStayDTO);
            }
        }
        if (availRoomStayDTOs.isEmpty()) {
            return null;
        }
        rs.getPayload().getHotelAvailRoomStay().setRoomStays(availRoomStayDTOs);
        return rs;
    }

    private boolean roomStayRestricted(StayDateRangeDTO stayDateRange, int propertyID, String roomTypeId, int los) {
        Date checkInDate = stayDateRange.getCheckInDate();
        Date checkOutDate = stayDateRange.getCheckOutDate();
        String hotelLevel = RoomStay.PREFIX + KeyValueHelper.SEPARATOR + propertyID + KeyValueHelper.SEPARATOR + 0;
        List<RoomStay> roomStays = roomStayCacheService.loadAll(hotelLevel);

        if (CollectionUtils.isEmpty(roomStays)) {
            return !CollectionUtils.isEmpty(roomStays)
                    && checkRoomStays(propertyID, roomTypeId, los, checkInDate, checkOutDate);
        }
        if (!inStayDateRange(roomStays, checkInDate, checkOutDate)) {
            return checkRoomStays(propertyID, roomTypeId, los, checkInDate, checkOutDate);
        }

        for (RoomStay roomStay : roomStays) {
            if (!losIsAvailable(los, roomStay)) {
                return true;
            }
        }
        return false;
    }

    private boolean inStayDateRange(List<RoomStay> roomStays, Date checkInDate, Date checkOutDate) {
        for (RoomStay roomStay : roomStays) {
            Date start = DateTimeUtils.parse(roomStay.getStart());
            Date end = DateTimeUtils.parse(roomStay.getEnd());
            if (checkInDate.compareTo(start) >= 0 && DateTimeUtils.addHours(checkOutDate, DELAY_ONE_DAY).compareTo(end) <= 0) {
                return true;
            }
        }
        return false;
    }

    private boolean checkRoomStays(int propertyID, String roomTypeId, int los, Date checkInDate, Date checkOutDate) {
        String roomStayLevel = RoomStay.PREFIX + KeyValueHelper.SEPARATOR + propertyID + KeyValueHelper.SEPARATOR + roomTypeId;
        for (RoomStay roomStay : roomStayCacheService.loadAll(roomStayLevel)) {
            Date start = DateTimeUtils.parse(roomStay.getStart());
            Date end = DateTimeUtils.parse(roomStay.getEnd());
            if (checkInDate.compareTo(start) >= 0
                    && DateTimeUtils.addHours(checkOutDate, DELAY_ONE_DAY).compareTo(end) <= 0
                    && !losIsAvailable(los, roomStay)) {
                return true;
            }
        }
        return false;
    }

    private boolean losIsAvailable(int los, RoomStay roomStay) {
        return (los >= roomStay.getMinStay() && roomStay.getMaxStay() == 0) ||
                (los >= roomStay.getMinStay() && los <= roomStay.getMaxStay());
    }

    public void setRoomStayCacheService(RoomStayCacheService roomStayCacheService) {
        this.roomStayCacheService = roomStayCacheService;
    }

    public void setParseRoomStayService(ParseRoomStayService parseRoomStayService) {
        this.parseRoomStayService = parseRoomStayService;
    }
}
